package org.xx.armory.spring5.jpa;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.transaction.annotation.Transactional;
import org.xx.armory.spring5.domain.Book;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;
import static org.springframework.beans.BeanUtils.copyProperties;

public class BookProvider {
    @Autowired
    private BookRepository bookRepository;

    private Specification<BookPO> findAllSpecification(
            String key,
            Date startDate,
            Date endDate
    ) {
        return (root, cq, cb) ->
                new PredicateBuilder()
                        .addIfNotBlank(key, cb.or(
                                cb.like(root.get(BookPO_.title), "%" + key + "%"),
                                cb.like(root.get(BookPO_.author), "%" + key + "%")
                        ))
                        .addIfNotNull(startDate, cb.greaterThanOrEqualTo(root.get(BookPO_.pubDate), startDate))
                        .addIfNotNull(endDate, cb.lessThan(root.get(BookPO_.pubDate), endDate))
                        .build(cb);

    }

    @Transactional
    public Optional<Book> save(
            Book book
    ) {
        final BookPO bookPO = new BookPO();
        copyProperties(book, bookPO);
        return Optional.of(this.bookRepository.saveAndFlush(bookPO)).map(this::toBook);
    }

    @Transactional(readOnly = true)
    public List<Book> findAll(
            String key,
            Date startDate,
            Date endDate,
            Pageable pageable
    ) {
        key = trimToEmpty(key);
        return this.bookRepository
                .findAll(
                        this.findAllSpecification(key, startDate, endDate),
                        pageable
                ).map(this::toBook)
                .filter(Objects::nonNull)
                .get()
                .collect(Collectors.toList());
    }

    @Transactional(readOnly = true)
    public int count(
            String key,
            Date startDate,
            Date endDate
    ) {
        key = trimToEmpty(key);
        return (int) this.bookRepository.count(this.findAllSpecification(key, startDate, endDate));
    }

    private Book toBook(
            BookPO po
    ) {
        if (po == null) {
            return null;
        }

        return new Book(
                po.getId(),
                po.getTitle(),
                po.getAuthor(),
                po.getPubDate(),
                po.getPrice(),
                po.getAmount()
        );
    }
}
